/*--------------------------------
*
* list.c
*   implementation for openPlant generic linked list package
*
*--------------------------------
*/
#include "primary_include_file.h"
#include "nodes/op_list.h"

/* Routines to simplify writing assertions about the type of a list;
* a NIL list is condidered to be an empty list of any type.
*/
/* _line_025 */
#define IsPointerList(l) ((l) == NIL || IsA((l), List))

#ifdef USE_ASSERT_CHECKING
/* check that the specified List is valid(so far as we can tell). */
static void check_list_invariants(List *list)
{
  if (list == NIL)
    return;
  Assert(list->length > 0);
  Assert(list->head != NULL);
  Assert(list->tail != NULL);

  Assert(list->type == T_List ||
    list->type == T_IntList ||
    list->type == T_OidList);
  if (list->length == 1) {
    Assert(list->head == list->tail);
  }
  if (list->length == 2) {
    Assert(list->head->next == list->tail);
  }
  Assert(list->tail->next == NULL);
}
#else
#define check_list_invariants(l)
#endif

/* Return a freshly allocated List. Since empty non-NIL lists are
* invalid, new_list() also allocates the head cell of the new list:
* the caller should be sure to fill in that cell's data.
*/
/* _line_62 */
static List* new_list(NodeTag type)
{
  List *new_list;
  ListCell *new_head;
  new_head = (ListCell *)malloc(sizeof(*new_head));
  new_head->next = NULL;
  /* new_head->data is left undifined! */
  new_list = (List *)malloc(sizeof(*new_list));
  new_list->type = type;
  new_list->length = 1;
  new_list->head = new_head;
  new_list->tail = new_head;

  return new_list;
}

/*
* Allocate a new cell and make it the head of the specified
* list. Assumes the list it is passed is non-NIL.
*
* The data in the new head cell is undefined; the caller should be
* sure to fill it in
*/
/* _line_089 */
static void new_head_cell(List *list)
{
  ListCell *new_head;

  new_head = (ListCell *)malloc(sizeof(*new_head));
  new_head->next = list->head;

  list->head = new_head;
  list->length++;
}

/* Allocate a new cell and make it the tail of the specified
* list. Assumes the list it is passed is non-NIL.
*
* The data in the new tail cell is undefined; the caller should
* be sure to fill it in.
*/
/* _line_107 */
static void new_tail_cell(List *list)
{
  ListCell *new_tail;

  new_tail = (ListCell *)malloc(sizeof(*new_tail));
  new_tail->next = NULL;

  list->tail->next = new_tail;
  list->tail = new_tail;
  list->length++;
}

/* Append a pointer to the list. A pointer to the modified list is
* returned. Note that this function may or may not destructively
* modify the list callers should always use this function's return
* value, rather than counting to use the pointer passed as the first
* argument.
*/
/* _line_127 */
List *lappend(List *list, void *datum)
{
  Assert(IsPointList(list));

  if (list == NIL) {
    list = new_list(T_List);
  } else {
    new_tail_cell(list);
  }

  lfirst(list->tail) = datum;
  check_list_invariants(list);
  return list;
}

/*
* Prepend a new element to the list. A pointer to the modified list
* is returned. Note that this function may or may not destructively
* modify the list; callers should always use this function's return
* value, rather than continuing to use the pointer passed as the
* second argument.
*/
/* _line_258 */
List *lcons(void *datum, List *list)
{
  Assert(IsPointerList(list));

  if (list == NIL) {
    list = new_list(T_List);
  } else {
    new_head_cell(list);
  }

  lfirst(list->head) = datum;
  check_list_invariants(list);

  return list;
}

/* concatenate list2 to the end of list1, and return list1, list1 is
* destructively changed. Callers should be sure to use the return
* value as the new pointer to the concatenated list: the 'list1'
* input pointer may or may not be the same as the returned pointer.
*
* The nodes in list2 are merely appended to the end of list1 in-place
* (i.e. they aren't copied; the tow lists will share some of the same
* storage). therefore, invoking list_free() on list2 will also invalidate
* a portion of list1
*/
/* _line_321 */
List *
list_concat(List *list1, List *list2)
{
  if (list1 == NIL) {
    return list2;
  }
  if (list2 == list1){
    printf("cannot list_concat() a list to itself");
  }
  if (list2 == NIL) {
    return list1;
  }

  Assert(list1->type == list2->type);

  list1->length += list2->length;
  list1->tail->next = list2->head;
  list1->tail = list2->tail;

  check_list_invariants(list1);
  return list1;
}

/* _line_1234 */
ListCell *
list_head(List *l)
{
  return l ? l->head : NULL;
}

/* _line_1240 */
ListCell *
list_tail(List *l)
{
  return l ? l->tail : NULL;
}

/* _line_1246 */
int
list_length(List *l)
{
  return l ? l->length : 0;
}

